package pl.edu.pw.ee.buildcity.scm.subversion;

import org.apache.commons.lang.time.FastDateFormat;
import org.apache.log4j.Logger;
import org.fest.assertions.Assertions;
import org.fest.assertions.Fail;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.tmatesoft.svn.core.wc.SVNRevision;
import pl.edu.pw.ee.buildcity.commons.Const;
import pl.edu.pw.ee.buildcity.entities.SCMCommit;
import pl.edu.pw.ee.buildcity.entities.SVNCommit;
import pl.edu.pw.ee.buildcity.scm.SCMException;

import javax.annotation.Resource;
import java.io.File;
import java.util.List;

import static org.fest.assertions.Assertions.assertThat;

/**
 * @author mgrzechocinski
 * @since 2009-09-19
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:scm-context.xml"})
public class SubversionClientIntegrationTestCase {

	private static final Logger log = Logger.getLogger(SubversionClientIntegrationTestCase.class);

	private static final String SVN_SERVER = "http://buildcity.googlecode.com";
	private static final String TEST_PROJECT_PATH = "/svn/trunk/wordcounter";

	@Resource(name = "subversionService")
	private SubversionService subversionService;

	@After
	public void removeWorkingCopy() {
		File workingCopy = new File(Const.WORKING_COPIES + TEST_PROJECT_PATH);
		workingCopy.delete();
	}

	@Test
	public void shouldCheckoutExampleProject() {
		//Given
		File workingCopy = null;

		//When
		try {
			SVNCheckoutParameters parameters = SVNCheckoutParameters.checkoutHEAD(SVN_SERVER + TEST_PROJECT_PATH);
			workingCopy = subversionService.checkout(parameters, Const.WORKING_COPIES + TEST_PROJECT_PATH);
		} catch (SCMException e) {
			e.printStackTrace();
			Fail.fail("Should checkout example path, but error occurred: " + e.getMessage());
		}

		//Then
		Assertions.assertThat(workingCopy).isNotNull().isDirectory();
		Assertions.assertThat(workingCopy.list()).hasSize(6).containsOnly(".svn", "pom.xml", "tekst.txt", "wordcounter-cmd", "wordcounter-core", "wordcounter-web");
	}

	@Test
	public void shouldReturnFalseWhileWorkingCopyIsInHEADRevision() {
		//Given
		File workingCopy = checkoutHEADRevisionOfTestProject();

		boolean shouldRebuildProject = true;

		//When
		try {
			shouldRebuildProject = subversionService.changeDetected(workingCopy);
		} catch (SCMException e) {
			Fail.fail("Should checkout example path, but error occurred: " + e.getMessage(), e);
		}

		//Then 
		assertThat(shouldRebuildProject).isFalse();
	}

	@Test
	public void shouldReturnTrueWhileWorkingCopyIsPREVIOUSRevision() throws SCMException {
		//Given
		File workingCopy = checkoutRevisionOfTestProject(SVNRevision.create(15));

		boolean shouldRebuildProject = false;

		//When
		try {
			shouldRebuildProject = subversionService.changeDetected(workingCopy);
		} catch (SCMException e) {
			Fail.fail("Should checkout example path, but error occured: " + e.getMessage(), e);
		}

		//Then 
		assertThat(shouldRebuildProject).isTrue();
		//Code should not be updated, so change is still detected
		assertThat(subversionService.changeDetected(workingCopy)).isTrue();
	}

	@Test
	public void shouldGetLogEntryForTestProject() {
		//Given
		File workingCopy = checkoutRevisionOfTestProject(SVNRevision.create(15));

		List<SCMCommit> logEntries = null;

		//When
		try {
			logEntries = subversionService.getLogBetweenHEADAndWorkingCopyRevision(workingCopy);
		} catch (SCMException e) {
			Fail.fail("Should get log entry, but error occurred: " + e.getMessage(), e);
		}

		//Then
		assertThat(logEntries).isNotNull();

		for (SCMCommit logEntry : logEntries) {
			log.debug("author: " + logEntry.getAuthor());
			log.debug("comment: " + logEntry.getComment());
			log.debug("date: " + logEntry.getDate());
			log.debug("changed: " + logEntry.getChanges());
			log.debug("revision: " + ((SVNCommit) logEntry).getRevision());
			log.debug("---------------------------");

			//Those should never be null (comment might be...)
			assertThat(logEntry.getAuthor()).isNotNull();
			assertThat(logEntry.getDate()).isNotNull();
			assertThat(logEntry.getChanges()).isNotNull();
		}

		//Errors when new commits are added, so we temporarily ignore it...
//		assertLogEntry(logEntries.get(0), 53, "", "2009-10-10");
//		assertLogEntry(logEntries.get(1), 54, "comment", "2009-10-10");
//		assertLogEntry(logEntries.get(2), 55, "comment2", "2009-10-11");
//		assertLogEntry(logEntries.get(3), 56, "", "2010-01-09");
//		assertLogEntry(logEntries.get(4), 57, "", "2010-02-28");
	}

	@Test
	public void shouldUpdateWorkingCopyToHEAD() throws SCMException {
		//Given
		File workingCopy = checkoutRevisionOfTestProject(SVNRevision.create(15));

		//When
		try {
			subversionService.updateToHEAD(workingCopy);
		} catch (SCMException e) {
			Fail.fail("Should update to HEAD, but error occurred: " + e.getMessage(), e);
		}

		//Then
		boolean changeDetected = subversionService.changeDetected(workingCopy);
		assertThat(changeDetected).isFalse();
	}

	private void assertLogEntry(SCMCommit logEntry, long expectedRevision, String expectedComment, String expectedDate) {
		assertThat(logEntry).isNotNull();
		assertThat(logEntry.getAuthor()).isEqualTo("mateusz");
		assertThat(logEntry.getComment()).isEqualTo(expectedComment);
		assertThat(((SVNCommit) logEntry).getRevision()).isEqualTo(expectedRevision);
		assertThat(logEntry.getChanges()).isNotNull();
		FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd");
		assertThat(fdf.format(logEntry.getDate())).isEqualTo(expectedDate);
	}

	private File checkoutHEADRevisionOfTestProject() {
		return checkoutRevisionOfTestProject(SVNRevision.HEAD);
	}

	private File checkoutRevisionOfTestProject(SVNRevision revision) {
		File workingCopy = null;
		try {
			SVNCheckoutParameters parameters = SVNCheckoutParameters.checkoutRevision(SVN_SERVER + TEST_PROJECT_PATH, revision);
			workingCopy = subversionService.checkout(parameters, Const.WORKING_COPIES + TEST_PROJECT_PATH);
		} catch (SCMException e) {
			Fail.fail("Could not checkout", e);
		}
		return workingCopy;
	}
}
